package com.zonski.jbots.game;

import com.zonski.jbots.engine.*;
import com.zonski.jbots.engine.entity.Mind;
import com.zonski.jbots.engine.entity.AbstractMind;
import com.zonski.jbots.game.entity.InputMind;
import com.zonski.jbots.game.entity.MinimalInputMind;

/**
 * Created by IntelliJ IDEA.
 * User: Chris Glover
 * Date: Oct 17, 2003
 * Time: 9:56:54 AM
 * To change this template use Options | File Templates.
 * <p>
 * Wouldn't typically make this extend Runnable, but done so for space considerations,
 * Controls the various states of the game
 */
public class JBotsGameController implements Runnable
{
    /**
     * Indicates that the game is loading
     */
    public static final int LOADING     = 0;

    /**
     * Plays a demo of the game
     */
    public static final int DEMO        = 1;

    /**
     * Introduction screen
     */
    public static final int INTRO       = 2;

    /**
     * Plays the game
     */
    public static final int GAME        = 3;

    /**
     * Pre level pause
     */
    public static final int PRE_LEVEL   = 4;

    /**
     * Game over dude
     */
    public static final int GAME_OVER   = 5;

    /**
     * restarts the game
     */
    public static final int GAME_START  = 6;

    private static final int DEFAULT_MAX_DEMO_UPDATES = 1000;

    public static final String DEMO_NAME = "demo";

    private int mode;
    private Engine engine;
    private RoomFactory roomFactory;
    private AbstractEngineRunner engineRunner;
    private int startLevel;
    private int level;
    private int cutTo;
    private int maxLevel = 1;
    private int modeUpdates;
    private int maxDemoUpdates = DEFAULT_MAX_DEMO_UPDATES;

    public JBotsGameController(Engine engine, RoomFactory roomFactory)
    {
        this.engine = engine;
        this.roomFactory = roomFactory;
    }

    public void setEngineRunner(AbstractEngineRunner engineRunner)
    {
        this.engineRunner = engineRunner;
    }

    public Engine getEngine()
    {
        return this.engine;
    }

    public void setStartLevel(int startLevel)
    {
        this.startLevel = startLevel;
    }

    public int getLevel()
    {
        return this.level;
    }

    public void setLevel(int level)
    {
        this.level = level%this.maxLevel;
        if(this.mode == GAME || this.mode == GAME_START)
        {
            try
            {
                this.engine.clearRoom();
                Room room = this.roomFactory.getRoom(
                        Integer.toString(this.level), this.engine
                );
                this.setMode(PRE_LEVEL);
                this.engine.setRoom(room);
            }catch(Exception ex){
                //System.out.println(ex);
                ex.printStackTrace();
            }
        }
    }

    public void setMaxLevel(int maxLevel)
    {
        this.maxLevel = maxLevel;
    }

    public int getModeUpdates()
    {
        return this.modeUpdates;
    }

    public int getMode()
    {
        return this.mode;
    }

    public void setMode(int mode)
    {
        if(this.mode != mode)
        {
            this.mode = mode;
            this.modeUpdates = 0;
            Thread thread = null;
            switch(mode)
            {
                case GAME_OVER:
                    // leave the game to keep playing
                    break;
                case LOADING:
                    // stop the game runner
                    if(this.engineRunner.isRunning())
                    {
                        this.engineRunner.stop();
                    }
                    break;
                case DEMO:
                    // load the demo game
                    try
                    {
                        this.engine.clearRoom();
                        Room room = this.roomFactory.getRoom(DEMO_NAME, this.engine);
                        this.engine.setRoom(room);
                    }catch(Exception ex){
                        ex.printStackTrace();
                        System.out.println("couldn't load demo room : "+ex);
                    }
                    if(!this.engineRunner.isRunning())
                    {
                        this.engineRunner.start();
                    }
                    break;
                case INTRO:
                    if(this.engineRunner.isRunning())
                    {
                        this.engineRunner.stop();
                    }
                    thread = new Thread(this);
                    thread.start();
                    break;
                case GAME_START:
                    this.setLevel(this.startLevel);
                    break;
                case GAME:
                    if(!this.engineRunner.isRunning())
                    {
                        this.engineRunner.start();
                    }
                    break;
                case PRE_LEVEL:
                    // pause the game for a while
                    if(this.engineRunner.isRunning())
                    {
                        this.engineRunner.stop();
                    }
                    System.gc();
                    // wait a while and restart
                    thread = new Thread(this);
                    thread.start();
                    break;
            }
        }
    }

    /**
     * should be called every time the engine is updated
     * @see AbstractEngineRunner
     */
    public void update()
    {
        this.modeUpdates ++;
        if(this.mode == GAME || mode == GAME_OVER)
        {
            // check whether there is at least one player controlled BOT alive or
            // at most one bot alive
            int numberPlayers = 0;
            int numberComputers = 0;
            for(int i=0; i<4; i++)
            {
                AbstractMind mind = (AbstractMind)this.engine.getMind(Engine.getPlayerMindName(i));
                if(mind != null && mind.getEntity() != null && !mind.getEntity().isRemovable())
                {
                    if(mind instanceof MinimalInputMind || mind instanceof InputMind)
                    {
                        numberPlayers++;
                    }else{
                        numberComputers++;
                    }
                }
            }
            if(this.mode == GAME_OVER)
            {
                if(numberComputers == 0)
                {
                    this.setMode(INTRO);
                }
            }else if(numberPlayers == 0){
                // everyone died, game over
                this.setMode(GAME_OVER);
            }else if(numberPlayers == 1 && numberComputers == 0){
                // move to the next level

                if(this.cutTo < this.modeUpdates)
                {
                    this.cutTo = this.modeUpdates + 120;
                }else if(this.cutTo == this.modeUpdates){
                    this.setLevel(this.level+1);
                    this.cutTo = 0;
                }
            }
        }else if(this.mode == DEMO){
            // check whether we're over time
            if(this.modeUpdates > this.maxDemoUpdates)
            {
                this.setMode(INTRO);
            }
        }
    }

    /**
     * changes the mode to the next mode
     */
    public void run()
    {
        try
        {
            switch(this.mode)
            {
                case INTRO:
                    // loop checking the input - horrible
                    long endTime = System.currentTimeMillis() + 10000;
                    while(System.currentTimeMillis() < endTime)
                    {
                        if(!this.engineRunner.checkIntro())
                        {
                            break;
                        }
                        Thread.sleep(100);
                    }
                    if(this.mode == INTRO)
                    {
                        this.setMode(DEMO);
                    }
                    break;
                case PRE_LEVEL:
                    Thread.sleep(2000);
                    if(this.mode == PRE_LEVEL)
                    {
                        this.setMode(GAME);
                    }
                    break;
            }
        }catch(Exception ex){
            System.out.println(ex);
        }
    }
}
